/*
 * @Description: 文件访问（前台直接展示如：图片、视频等）
 * @Autor: HuiSir<273250950@qq.com>
 * @Date: 2022-07-20 14:40:21
 * @LastEditTime: 2022-08-02 15:02:17
 */
import fs from 'node:fs'
import contentTypes from '../helpers/contentType.js' //常用contentType对照表
import {
    useResponseError,
    useClassDecorators,
    useMethodDecorators,
    useParameterDecorators,
    type KoaControl
} from 'koa-control'
import { AccessDto } from './dto/index.js'
import fileModel from '../models/File.js'

const { e404 } = useResponseError()
const { Controller } = useClassDecorators()
const { Get, Bind } = useMethodDecorators()
const { Params, Response } = useParameterDecorators()

@Controller()
export default class Access {
    /**
     * @description: 通过chunkId访问图片（白名单，无需验证token）
     * @return {fs.ReadStream}
     * @author: HuiSir
     */
    @Get('/image/:chunkId')
    @Bind(AccessDto)
    async image(@Params('chunkId') chunkId: string, @Response() Response: KoaControl.Response): Promise<fs.ReadStream> {
        return await fileAccess(chunkId, 'image', Response)
    }

    /**
     * @description: 通过chunkId访问视频（白名单，无需验证token）
     * @return {fs.ReadStream}
     * @author: HuiSir
     */
    @Get('/video/:chunkId')
    @Bind(AccessDto)
    async video(@Params('chunkId') chunkId: string, @Response() Response: KoaControl.Response): Promise<fs.ReadStream> {
        return await fileAccess(chunkId, 'video', Response)
    }

    /**
     * @description: 通过chunkId访问音频（白名单，无需验证token）
     * @return {fs.ReadStream}
     * @author: HuiSir
     */
    @Get('/audio/:chunkId')
    @Bind(AccessDto)
    async audio(@Params('chunkId') chunkId: string, @Response() Response: KoaControl.Response): Promise<fs.ReadStream> {
        return await fileAccess(chunkId, 'audio', Response)
    }
}

/**
 * 公共方法
 */
async function fileAccess(
    chunkId: string,
    type: 'image' | 'video' | 'audio',
    Response: KoaControl.Response
) {
    const file: KoaControl.File = await fileModel.findOne({ chunkId }, '-state')

    //判断路径是否存在，不存在直接返回
    if (!fs.existsSync(file.path)) {
        e404()
        return
    }

    //判断路径是否为文件，若为文件夹，则返回
    const stat = fs.lstatSync(file.path)
    if (stat.isDirectory()) {
        e404()
        return
    }

    const contentType = contentTypes[file.extName]

    // 判断文件类型
    if (contentType.split('/')[0] != type) {
        e404()
        return
    }

    //这里需要设置content-type,否则前端接收的文件无法识别
    Response.set('Content-Type', contentType)

    //以流的形式返回前端
    return fs.createReadStream(file.path)
}